package com.itclass.auth.config;


import com.itclass.auth.authentication.*;

import com.itclass.auth.authentication.filter.CustomLoginConfigurer;
import com.itclass.auth.authentication.provider.support.GitHubAuthAuthenticationProvider;
import com.itclass.auth.authentication.provider.support.LdapAuthenticationProvider;
import com.itclass.auth.authentication.provider.support.UsernamePasswordAuthenticationProvider;
import com.itclass.auth.context.AuthServerApplicationContext;
import com.itclass.auth.encoder.SimplPasswordEncoder;
import com.itclass.auth.handler.RedisLogoutHandler;
import com.itclass.auth.handler.RedisLogoutSuccessHandler;
import com.itclass.auth.handler.authentication.support.LoginAuthenticationFailureHandler;
import com.itclass.auth.handler.authentication.support.LogingAuthenticationSuccessHandler;
import com.itclass.auth.handler.authorize.AuthorizeAccessDeniedHandler;
import com.itclass.auth.pojo.dto.MenuRoleDTO;
import com.itclass.auth.repository.RedisSecurityContextRepository;
import com.itclass.auth.service.SysMenuService;
import com.itclass.auth.service.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.*;

import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;



    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;
    @Autowired
    private GitHubAuthAuthenticationProvider gitHubAuthAuthenticationProvider;
    @Autowired
    private LdapAuthenticationProvider ldapAuthenticationProvider;


    @Autowired
    private LogingAuthenticationSuccessHandler logingAuthenticationSuccessHandler;


    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        // 查询项目所有权限接口
        List<MenuRoleDTO> menuAll = sysMenuService.queryMenuAll();

        // 要登录认证才能访问的url
        List<MenuRoleDTO> authenticatedUrl = new ArrayList<>();
        // 要认证授权才能访问 url
        List<MenuRoleDTO> hasRoleUrl = new ArrayList<>();
        for (MenuRoleDTO menuRoleDTO : menuAll) {
            List<String> roleCodes = menuRoleDTO.getRoleCodes();
            if (CollectionUtils.isEmpty(roleCodes)) {
                authenticatedUrl.add(menuRoleDTO);
            }else {
                hasRoleUrl.add(menuRoleDTO);
            }
        }


        // 移除一些实际用不上的配置过滤器类
        http.anonymous().disable();
        http.sessionManagement().disable();
        http.csrf().disable();

        //创建redis 用户认证上下文仓库。
        RedisSecurityContextRepository redisSecurityContextRepository = new RedisSecurityContextRepository(redisTemplate);

        http.setSharedObject(SecurityContextRepository.class,redisSecurityContextRepository);






        // 权限路径配置
        http.authorizeHttpRequests(new Customizer<AuthorizeHttpRequestsConfigurer<org.springframework.security.config.annotation.web.builders.HttpSecurity>.AuthorizationManagerRequestMatcherRegistry>() {
            @Override
            public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry authorizationManagerRequestMatcherRegistry) {

                if (!CollectionUtils.isEmpty(authenticatedUrl)) {
                    // 这些请求 都要登录 才能访问
                    String[] urls =  authenticatedUrl.stream().map(MenuRoleDTO::getPath).collect(Collectors.toList()).toArray(new String[authenticatedUrl.size()]);
                    authorizationManagerRequestMatcherRegistry.mvcMatchers(urls).authenticated();
                }

                // 这些请求 都要登录授权 才能访问
                for (MenuRoleDTO menuRoleDTO : hasRoleUrl) {
                    String[] roles = menuRoleDTO.getRoleCodes().toArray(new String[menuRoleDTO.getRoleCodes().size()]);
                    authorizationManagerRequestMatcherRegistry.mvcMatchers(menuRoleDTO.getPath()).hasAnyRole(roles);
                }
            }
        });


//        // 配置登录认证
//        http.formLogin(new Customizer<FormLoginConfigurer<HttpSecurity>>() {
//            @Override
//            public void customize(FormLoginConfigurer<HttpSecurity> formLoginConfigurer) {
//                formLoginConfigurer.loginProcessingUrl("/login");
//                formLoginConfigurer.loginPage("/loginPage");
//                // 添加认证失败处理器
//                formLoginConfigurer.failureHandler(new LoginAuthenticationFailureHandler(stringRedisTemplate));
//                // 添加认证成功处理器
//                formLoginConfigurer.successHandler(new LogingAuthenticationSuccessHandler(stringRedisTemplate));
//
//                formLoginConfigurer.securityContextRepository(redisSecurityContextRepository);
//            }
//        });

        /**
         *  由于 springSecurity 认证管理过滤器不符合要求，所以不使用框架自带的，自己自定义一个 认证管理过滤器
         *  添加 自定义 认证管理过滤器
         */
        CustomLoginConfigurer<HttpSecurity> customLoginConfigurer = new CustomLoginConfigurer<>();
        http.apply(customLoginConfigurer);

        customLoginConfigurer.addRequestMatcher(new AntPathRequestMatcher("/auth/github/callback","GET"));
        // 添加认证失败处理器
        customLoginConfigurer.failureHandler(new LoginAuthenticationFailureHandler(stringRedisTemplate));
        // 添加认证成功处理器
        customLoginConfigurer.successHandler(logingAuthenticationSuccessHandler);

        customLoginConfigurer.securityContextRepository(redisSecurityContextRepository);



        // 退出认证
        http.logout(new Customizer<LogoutConfigurer<HttpSecurity>>() {
            @Override
            public void customize(LogoutConfigurer<HttpSecurity> logoutConfigurer) {

                logoutConfigurer.addLogoutHandler(new RedisLogoutHandler(redisSecurityContextRepository));
                logoutConfigurer.logoutSuccessHandler(new RedisLogoutSuccessHandler());
            }
        });

        //配置授权异常处理
        http.exceptionHandling(new Customizer<ExceptionHandlingConfigurer<HttpSecurity>>() {
            @Override
            public void customize(ExceptionHandlingConfigurer<HttpSecurity> exceptionHandlingConfigurer) {


                // 权限不足处理器
                exceptionHandlingConfigurer.accessDeniedHandler(new AuthorizeAccessDeniedHandler());
                // 没有登录认证处理器
                exceptionHandlingConfigurer.authenticationEntryPoint(new LoginAuthenticationEntryPoint());

            }
        });

        // 添加用户名密码认证管理器
        http.authenticationProvider(usernamePasswordAuthenticationProvider);
        // 添加git hub 认证登录管理器
        http.authenticationProvider(gitHubAuthAuthenticationProvider);
        http.authenticationProvider(ldapAuthenticationProvider);
        DefaultSecurityFilterChain filterChain = http.build();
        return filterChain;
    }
}